package client;

import java.awt.*;
import java.awt.event.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;

import javax.swing.*;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.MouseInputAdapter;
import javax.swing.text.*;

import common.MessageElement;
import common.StyleBean;
import common.User;
import common.Message;
import static common.StyleBean.LINK_ATTRIBUTE;

public class ChatWindow extends JFrame implements ActionListener{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	private final User chattingClient;
	private JTextPane messagePane;
	private JTextPane composePane;
	private final IMClientGUI gui;
	private Style localStyle;
	private Style systemStyle;
	private Style linkStyle;
	private StyleChooser styleChooser;
	private String[] supportFonts;
	
	public ChatWindow(IMClientGUI userGUI, User user, String[] fonts){
		super();
		this.chattingClient = user;
		this.gui = userGUI;
		this.supportFonts = fonts;
		
		systemStyle = StyleContext.getDefaultStyleContext().addStyle("systemStyle", null);
		StyleConstants.setForeground(systemStyle, Color.gray);

		localStyle = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
		StyleConstants.setForeground(localStyle, Color.blue);
		StyleBean preferedStyle = userGUI.getConfig().getPreferedStyle();
		if(preferedStyle != null){
			localStyle.addAttributes(preferedStyle.getAttributeSet());
		}
		
		linkStyle = StyleContext.getDefaultStyleContext().addStyle("linkStyle", null);
		StyleConstants.setForeground(linkStyle, Color.blue);
		StyleConstants.setUnderline(linkStyle, true);
		linkStyle.addAttribute(LINK_ATTRIBUTE, true);

		messagePane = new JTextPane();
		messagePane.setEditable(false);
		messagePane.setLogicalStyle(systemStyle);
		messagePane.addMouseListener(new TextClickListener());
		messagePane.addMouseMotionListener(new TextMotionListener());
		JScrollPane msgPaneScroller = new JScrollPane(messagePane);
		msgPaneScroller.setPreferredSize(new Dimension(500,160));

		composePane = new JTextPane();
		composePane.addKeyListener(new KeyAdapter(){
			@Override
			public void keyPressed(KeyEvent e) {
				if(e.getKeyCode() == KeyEvent.VK_ENTER){
					if((e.getModifiers() & KeyEvent.SHIFT_MASK)==0){
						sendMessage();
						e.consume();
					}else{
						StyledDocument doc = composePane.getStyledDocument();
						try {
							doc.insertString(doc.getLength(), System.getProperty("line.separator"), localStyle);
						} catch (BadLocationException e1) {
						}
					}
				}
			}
		});
		composePane.setLogicalStyle(localStyle);
		composePane.setCharacterAttributes(localStyle, true);
		JScrollPane cmpsPaneScroller = new JScrollPane(composePane);
		cmpsPaneScroller.setPreferredSize(new Dimension(450,60));
		
		JButton sendBtn = new JButton("send");
		sendBtn.setActionCommand("send");
		sendBtn.addActionListener(this);
		
		JPanel sendPanel = new JPanel();
		sendPanel.setLayout(new BoxLayout(sendPanel,BoxLayout.X_AXIS));
		sendPanel.add(cmpsPaneScroller);
		sendPanel.add(sendBtn);
		
		JPanel compPane = new JPanel();
		compPane.setLayout(new BorderLayout());
		styleChooser = new StyleChooser(StyleConstants.getForeground(localStyle),this);
		compPane.add(styleChooser,BorderLayout.PAGE_START);
		compPane.add(sendPanel,BorderLayout.CENTER);

		JSplitPane mainPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT,msgPaneScroller,compPane);
		mainPanel.setOneTouchExpandable(true);
		mainPanel.setResizeWeight(0.6);
		mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
		
		this.setLocationRelativeTo(userGUI);
		this.setLocationByPlatform(true);
		setContentPane(mainPanel);
		setTitle("Chatting with " + chattingClient.getAlias());
		//setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				// gui.removeChatWindow(chattingClient.getUserId());
				StyleBean style = new StyleBean();
				style.loadAttributeSet(localStyle);
				style.setUnderine(false);
				style.setLink(false);
				gui.getConfig().setPreferedStyle(style);
				setVisible(false);
			}
		});
	    
	    composePane.addCaretListener(new CaretListener(){

			@Override
			public void caretUpdate(CaretEvent e) {
				int pos = composePane.getCaretPosition();
				if(pos > 0) pos--;
				AttributeSet attr = composePane.getStyledDocument().getCharacterElement(pos).getAttributes();
				if(StyleConstants.getIcon(attr)==null&&!StyleConstants.isUnderline(attr)){
					styleChooser.loadStyle(attr);
					localStyle.addAttributes(attr);
				}
			}
	    });
	    
		Image[] imgs = userGUI.getIconImages().toArray(new Image[userGUI.getIconImages().size()]);
		if(imgs.length > 0)
			setIconImage(imgs[imgs.length-1]);
		pack();
	}
	
	public void addSystemMessage(String msg){
		StyledDocument doc = messagePane.getStyledDocument();
		try {
			doc.insertString(doc.getLength(),"("+DateFormat.getTimeInstance().format(new Date())+") ",systemStyle);
			doc.insertString(doc.getLength(),msg,systemStyle);
			doc.insertString(doc.getLength(),System.getProperty("line.separator"),systemStyle);
		} catch (BadLocationException e) {
		}
	}
	
	public void addRemoteMessage(Message msg){
		StyledDocument doc = messagePane.getStyledDocument();
		try{
			doc.insertString(doc.getLength(),"("+DateFormat.getTimeInstance().format(msg.getTime())+") ",systemStyle);
			doc.insertString(doc.getLength(),chattingClient.getAlias()+ ": ",systemStyle);
			for(MessageElement me : msg.getMessage()){
				doc.insertString(doc.getLength(), me.getText(), me.getStyle().getAttributeSet());
			}
		}catch(BadLocationException e){
			e.printStackTrace();
		}
		messagePane.setCaretPosition(messagePane.getStyledDocument().getLength());
	}
	public void showWindow(){
		setVisible(true);
		composePane.requestFocus();
		
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		if(e.getActionCommand().equals("send")){
			sendMessage();
			composePane.requestFocus();
		}
		
	}

	private void sendMessage() {
		if(composePane.getText().length()<=0) return;
		StyledDocument doc = messagePane.getStyledDocument();
		ArrayList<Element> paragraphs = new ArrayList<Element>();
		findParagraphs(composePane.getStyledDocument().getDefaultRootElement(),paragraphs);
		Message msg = new Message(gui.getUser().getUserId());
		ArrayList<MessageElement> mes = new ArrayList<MessageElement>();
		try {
			doc.insertString(doc.getLength(),"("+DateFormat.getTimeInstance().format(new Date())+") ",systemStyle);
			doc.insertString(doc.getLength(),gui.getUser().getAlias()+ ": ",systemStyle);
			for(Element e : paragraphs){
				for(int i=0; i<e.getElementCount(); i++){
					Element ele = e.getElement(i);
					Document d = e.getElement(i).getDocument();
					try {
						String text = d.getText(ele.getStartOffset(), ele.getEndOffset()-ele.getStartOffset());
						MessageElement m = new MessageElement(text,new StyleBean(ele.getAttributes()));
						mes.add(m);
						doc.insertString(doc.getLength(), text, m.getStyle().getAttributeSet());
						if(text.contains("\n"))
							//System.out.println("new line");
							messagePane.setParagraphAttributes(ele.getAttributes(), true);
					} catch (BadLocationException e1) {
						e1.printStackTrace();
					}
				}
			}
		} catch (BadLocationException e2) {
			e2.printStackTrace();
		}
		msg.setMessage(mes);
		gui.sendMessage(chattingClient.getUserId(), msg);
		composePane.setText("");
		composePane.setCaretPosition(0);
		composePane.setCharacterAttributes(localStyle, true);
	}
	
	private void findParagraphs(Element e, ArrayList<Element> list){
		for(int i=0; i<e.getElementCount();i++){
			if(e.getElement(i).getName().equals("paragraph")) list.add(e.getElement(i));
			else findParagraphs(e.getElement(i),list);
		}
	}

	public void insertIcon(ImageIcon icon){
		SimpleAttributeSet attr = new SimpleAttributeSet();
		StyleConstants.setIcon(attr, icon);
		attr.addAttribute("pos", composePane.getCaretPosition());
		attr.addAttributes(localStyle.copyAttributes());
		try {
			composePane.getStyledDocument().insertString(composePane.getCaretPosition(), " ", attr);
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
		composePane.setCharacterAttributes(localStyle, true);
		composePane.requestFocus();
	}

	public void changeStyle() {
		composePane.setCharacterAttributes(localStyle, true);
		composePane.requestFocus();
	}

	public Style getLocalStyle() {
		return localStyle;
	}

	public String[] getSupportFonts() {
		return supportFonts;
	}
	
	public void insertHyperLink(){
		String text = composePane.getSelectedText();
		if(text!=null && !text.trim().equals("")){
			try {
				URL url = new URL(text);
				if(url.getProtocol().equals("http")||url.getProtocol().equals("ftp")){
					composePane.setCharacterAttributes(linkStyle, true);
				}
			} catch (MalformedURLException e) {
			}
		}
	}

	private class TextClickListener extends MouseAdapter {
		public void mouseClicked(MouseEvent e) {
			try {
				Element elem = messagePane.getStyledDocument().getCharacterElement(messagePane.viewToModel(e
						.getPoint()));
				Boolean isurl = (Boolean) elem.getAttributes().getAttribute(LINK_ATTRIBUTE);
				if (isurl != null && isurl){
					URL url = new URL(elem.getDocument().getText(elem.getStartOffset(), elem.getEndOffset()-elem.getStartOffset()));
					if(url.getProtocol().equals("http")||url.getProtocol().equals("ftp"))
						new URLLinkAction(url.toString()).execute();
				}
			} catch (Exception x) {
				x.printStackTrace();
			}
		}
	}

	private class TextMotionListener extends MouseInputAdapter {
		public void mouseMoved(MouseEvent e) {
			Element elem = messagePane.getStyledDocument().getCharacterElement(messagePane.viewToModel(e
					.getPoint()));
			AttributeSet as = elem.getAttributes();
			if (StyleConstants.isUnderline(as))
				messagePane.setCursor(new Cursor(Cursor.HAND_CURSOR));
			else
				messagePane.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
		}
	}
}
